<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\store;

use app\dao\store\SystemStoreDao;
use app\dao\user\UserVisitStoreDao;
use app\services\BaseServices;
use app\services\order\store\BranchOrderServices;
use app\services\order\StoreDeliveryOrderServices;
use app\services\order\StoreOrderCartInfoServices;
use app\services\order\StoreOrderServices;
use app\services\other\CityAreaServices;
use app\services\other\QrcodeServices;
use app\services\product\branch\StoreBranchProductServices;
use app\services\product\product\StoreProductServices;
use app\services\product\category\StoreProductCategoryServices;
use app\services\product\sku\StoreProductAttrValueServices;
use app\services\store\finance\StoreFinanceFlowServices;
use app\services\system\SystemRoleServices;
use app\services\user\UserServices;
use app\services\user\UserVisitStoreServices;
use crmeb\exceptions\AdminException;
use crmeb\services\CacheService;
use crmeb\services\erp\Erp;
use crmeb\services\FormBuilder;
use crmeb\services\SystemConfigService;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Log;


/**
 * 门店
 * Class SystemStoreServices
 * @package app\services\system\store
 * @mixin SystemStoreDao
 */
class SystemStoreServices extends BaseServices
{
	/**
	 * 进店规则
	 * @var string
	 */
	public $cacheTag = 'entry_store';

    /**
     * 创建form表单
     * @var Form
     */
    protected $builder;

    /**
     * 构造方法
     * SystemStoreServices constructor.
     * @param SystemStoreDao $dao
     * @param FormBuilder $builder
     */
    public function __construct(SystemStoreDao $dao, FormBuilder $builder)
    {
        $this->dao = $dao;
        $this->builder = $builder;
    }


	/**
	 * 获取门店二维码
	 * @param int $id
	 * @param int $staff_id
	 * @return array
	 */
	public function  getStoreQrcode(int $id, int $staff_id = 0)
	{
		//生成h5地址
		$weixinPage = "/pages/store/home/index?id=" . $id . '&staff_id=' . $staff_id;
		$weixinFileName = "wechat_store_cate_id_" . $id . ".png";
		/** @var QrcodeServices $QrcodeService */
		$QrcodeService = app()->make(QrcodeServices::class);
		$wechatQrcode = $QrcodeService->getWechatQrcodePath($weixinFileName, $weixinPage, false, false);
		//生成小程序地址
		$routineQrcode = $QrcodeService->getRoutineQrcodePath($id, 0, 10, $weixinFileName, true, ['staff_id' => $staff_id]);
		return ['wechat' => $wechatQrcode, 'routine' => $routineQrcode, 'url' => $weixinPage];
	}

	/**
	 * 获取单个门店信息
	 * @param int $id
	 * @return array|\think\Model|null
	 * @throws \think\db\exception\DataNotFoundException
	 * @throws \think\db\exception\DbException
	 * @throws \think\db\exception\ModelNotFoundException
	 */
	public function getStoreInfo(int $id)
	{
		$storeInfo = $this->dao->getOne(['id' => $id, 'is_del' => 0]);
		if (!$storeInfo) {
			throw new ValidateException('获取门店信息失败');
		}

//		$storeInfo['qrcode'] = $this->getStoreQrcode($id);

		$storeInfo['day_time'] = $storeInfo['day_time'] ? explode('-', $storeInfo['day_time']) : [];
		$storeInfo['addressSelect'] = [$storeInfo['province'], $storeInfo['city'], $storeInfo['area'], $storeInfo['street']];
		return $storeInfo->toArray();
	}

	/**
	 * 获取默认的进店规格参数
	 * @param array $param
	 * @return array
	 */
	public function getDefaultEntryRulesParam(array $param = [])
	{
		$default =  [
			'latitude' => '',
			'longitude' => '',
			'store_id' => 0,
			'select_store_id' => 0,
			'ip' => request()->ip()
		];
		return  $param ? array_merge($default, $param) : $default;
	}

	/**
	 * 处理定位经纬度，成城市
	 * @param string $latitude
	 * @param string $longitude
	 * @return array|false|string[]
	 * @throws \think\db\exception\DataNotFoundException
	 * @throws \think\db\exception\DbException
	 * @throws \think\db\exception\ModelNotFoundException
	 */
	public function handleLocation(string $latitude, string $longitude)
	{
		$nowCity = [];
		if (!$latitude || !$longitude) {
			return $nowCity;
		}
		//存在经纬度验证定位区域
		$result = $this->geoLbscoder($latitude, $longitude);
		$address = $result['address'] ?? '';
		//解析初地址
		if ($address) {
			if (strpos($address, '/') === false) {
				$address = implode('/', array_values($this->addressHandle($address)));
			}
			/** @var CityAreaServices $cityAreaServices */
			$cityAreaServices = app()->make(CityAreaServices::class);
			$city = $cityAreaServices->searchCity(compact('address'));
			if ($city) {
				$nowCity = array_merge(explode('/', trim($city['path'], '/')), [$city['id']]);
			}
		}
		return $nowCity;
	}

	/**
	 * 清空进店规则缓存
	 * @return bool
	 */
	public function clearEntryStoreCache()
	{
		CacheService::redisHandler($this->cacheTag)->clear();
		return true;
	}

	/**
	 * 根据进店规则：获取当前进入的门店ID
	 * @param int $uid
	 * @param array $param
	 * @return array
	 */
	public function getStoreIdByEntryRules(int $uid = 0, array $param = [], bool $isCache = false)
	{
		$storeId = 0;
		$default = '';
		$param = $this->getDefaultEntryRulesParam($param);
		$selectStoreId = (int)($param['select_store_id'] ?? 0);
		unset($param['select_store_id']);
		$userEntryRules = SystemConfigService::get('user_entry_rules', []);
		$shopOperationType = sys_config('shop_operation_type', 1);
		if ($uid) {
			$cacheKey = md5('entry_store' . $uid);
		} else {
			$cacheKey = md5('entry_store_' . json_encode($param));
		}
		$user_entry_name = '';
		$data = CacheService::get($cacheKey);
		if (!$data || $isCache) {//移动端请求更新缓存
			//用户移动端切换门店
			if ($selectStoreId) {
				$storeId = $selectStoreId;
			} else {
				if ($userEntryRules) {//设置进店规则
					$nowCity = [];
					if ($param['latitude'] && $param['longitude']) {
						//存在经纬度验证定位区域
						$nowCity = $this->handleLocation((string)$param['latitude'], (string)$param['longitude']);
					}
					foreach ($userEntryRules as $rule) {
						//禁用状态跳过
						if (!isset($rule['is_use']) || !$rule['is_use']) continue;
						switch ($rule['name']) {
							case 'user_belong_store'://用户归属门店
								if ($uid) {
									/** @var UserServices $userServices */
									$userServices = app()->make(UserServices::class);
									$userInfo = $userServices->getUserCacheInfo($uid);
									$storeId = $userInfo['belong_store_id'] ?? 0;
								}
								break;
							case 'user_param_store'://带参门店
								$storeId = $param['store_id'] ?? 0;
								break;
							case 'user_visit_store'://最近访问门店
								if ($uid) {
									/** @var UserVisitStoreServices $userVisitStoreServices */
									$userVisitStoreServices = app()->make(UserVisitStoreServices::class);
									$storeId = $userVisitStoreServices->getUserNearVisitStore($uid);
									if ($rule['is_alone'] && $storeId) {//仅隔离门店进入
										$count = $this->dao->count(['is_del' => 0, 'id' => $storeId, 'is_alone' => 1]);
										if (!$count) $storeId = 0;
									}
								}
								break;
							case 'region_recommend_store'://地区定向推荐门店
								$storeId = $rule['store_id'] ?? 0;
								if ($rule['region_id'] && $storeId) {
									if ($nowCity) {
										$regionCity = array_map(function ($item) {
											return end($item);
										}, $rule['region_id']);
										//不再推荐区域
										if (!array_intersect($regionCity, $nowCity)) {
											$storeId = 0;
										}
									} else {
										$storeId = 0;
									}
								}
								break;
							case 'user_locate_store'://用户定位推荐门店
								$where = [];
								if ($rule['is_region_alone'] && $param['latitude'] && $param['longitude']) {//开启区域隔离推荐
									//存在经纬度验证定位区域
									/** @var SystemStoreRegionServices $regionServices */
									$regionServices = app()->make(SystemStoreRegionServices::class);
									$region_id = $regionServices->getRegionByCity($nowCity);
									if ($region_id) {
										$where['region_id'] = $region_id;
									}
								}
								$nearStore = $this->getNearbyStore($where, $param['latitude'] ?? '', $param['longitude'] ?? '', $param['ip'] ?? '', 1);
								$storeId = $nearStore['id'] ?? 0;
								break;
							case 'default_store'://无门店推荐结果，直接进入指定页面
								$storeId = $rule['default_type'] == 1 ? ($rule['store_id'] ?? 0) : 0;
								$default = $rule['url'] ?? '';
								break;
						}
						$storeId = (int)$storeId;
						if ($storeId && $this->dao->count(['store_id' => $storeId, 'is_del' => 0, 'is_show' => 1])) {//匹配到进入门店ID 结束
							$user_entry_name = $rule['name'] ?? '';
							break;
						}
					}
				} else {//未设置
					$nearStore = $this->getNearbyStore([], $param['latitude'] ?? '', $param['longitude'], $param['ip'] ?? '', 1);
					$storeId = $nearStore['id'] ?? 0;
				}
			}
			$data = ['store_id' => $storeId];
			CacheService::redisHandler($this->cacheTag)->set($cacheKey, $data, 3 * 24 * 3600);
		}
		return array_merge($data, ['default' => $default, 'shop_operation_type' => $shopOperationType, 'user_entry_rules' => $userEntryRules, 'user_entry_name' => $user_entry_name]);
	}

	/**
	 * 根据门店，获取当前门店所在区域内所有门店
	 * @param int $storeId
	 * @return array|void
	 * @throws \think\db\exception\DataNotFoundException
	 * @throws \think\db\exception\DbException
	 * @throws \think\db\exception\ModelNotFoundException
	 */
	public function getRegionStoreIds(int $storeId)
	{
		$ids = [];
		$storeInfo = $this->dao->get($storeId);
		if (!$storeInfo) {
			return $ids;
		}
		//门店未设置所在区域
		if (!$storeInfo['region_id']) {
			return $ids;
		}
		//所在区域是设置为隔离，切换本区域内门店
		if ($storeInfo['is_region_alone']) {
			$ids = $this->dao->getColumn(['is_show' => 1, 'is_del' => 0, 'region_id' => $storeInfo['region_id']], 'id');
		} else {//不隔离，获取所有区域不隔离门迪娜
			$ids = $this->dao->getColumn(['is_show' => 1, 'is_del' => 0, 'is_region_alone' => 0], 'id');
		}
		return $ids;
	}

	/**
	 * 首页获取门店列表
	 * @param array $where
	 * @param string $latitude
	 * @param string $longitude
	 * @param string $ip
	 * @param int $limit
	 * @param int $product_id
	 * @param int $store_id
	 * @return array|mixed
	 * @throws \think\db\exception\DataNotFoundException
	 * @throws \think\db\exception\DbException
	 * @throws \think\db\exception\ModelNotFoundException
	 */
	public function getHomeStoreList(int $uid, array $where = [], string $latitude = '', string $longitude = '', string $ip = '', int $limit = 0, int $product_num = 0)
	{
		$where = array_merge($where, ['status' => 1, 'is_del' => 0]);
		if ($limit) {
			$page = 1;
			$field = ['*'];
		} else {
			[$page, $limit] = $this->getPageValue();
			$field = ['id', 'name', 'phone', 'image', 'background_image', 'latitude', 'longitude', 'address', 'detailed_address', 'is_show', 'day_time', 'day_start', 'day_end', 'valid_range', 'delivery_type', 'use_system_money', 'is_alone'];
		}
		//默认附近门店
		$store_type = $where['store_type'] ?? 1;
		$uid = $where['uid'] ?? 0;
		unset($where['store_type'], $where['uid']);
		if ($store_type != 1) {//常用门店
			if ($uid) {
				/** @var StoreUserServices $storeUserServices */
				$storeUserServices = app()->make(StoreUserServices::class);
				$ids = $storeUserServices->getColumn(['uid' => $uid], 'store_id');
				if (!$ids) {
					return [];
				}
				$where['ids'] = $ids;
			} else {//没登录，无常用门店
				return [];
			}
		}
		$with = [];
		$storeList = [];
		if (isset($where['ids'])) {
			$where['ids'] = array_unique(array_diff($where['ids'], [0]));
		}
		if (isset($where['id']) && $where['id']) {
			$storeList = $this->dao->getStoreList($where, $field, $page, $limit, $with, $latitude, $longitude, $latitude && $longitude ? 1 : 0);
		} elseif ($latitude && $longitude) {
			$storeList = $this->dao->getStoreList($where, $field, $page, $limit, $with, $latitude, $longitude, 1);
		} elseif ((isset($where['province']) && $where['province']) || (isset($where['city']) && $where['city']) || (isset($where['area']) && $where['area'])) {
			$storeList = $this->dao->getStoreList($where, $field, $page, $limit, $with, $latitude, $longitude, $latitude && $longitude ? 1 : 0);
		} elseif ((isset($where['province']) && $where['province']) && (isset($where['city']) && $where['city']) && (isset($where['area']) && $where['area']) && !$latitude && !$longitude) {
			$storeList = $this->dao->getStoreList($where, $field, $page, $limit);
		} elseif ($ip) {
			$strField = $limit == 1 ? '*' : implode(',', $field);
			$addressArr = $this->addressHandle($this->convertIp($ip));
			$city = $addressArr['city'] ?? '';
			if ($city) {
				$storeList = $this->dao->getStoreByAddressInfo($city, $where, $strField, $page, $limit, $with);
			}
			$province = $addressArr['province'] ?? '';
			if (!$storeList && $province) {
				$storeList = $this->dao->getStoreByAddressInfo($province, $where, $strField, $page, $limit, $with);
			}
		}
		//上面条件都没获取到门店
		if (!$storeList) {
			$storeList = $this->dao->getStoreList($where, $field, $page, $limit, $with);
		}
		if ($storeList) {
			/** @var StoreProductServices $productServices */
			$productServices = app()->make(StoreProductServices::class);
			$productWhere = ['is_del' => 0, 'is_show' => 1, 'is_verify' => 1, 'show_type' => [0, 1]];
			foreach ($storeList as &$item) {
				$item['range'] = 0;
				if (isset($item['distance'])) {
					$item['range'] = bcdiv($item['distance'], '1000', 1);
				} else {
					$item['range'] = 0;
				}
				if (isset($item['is_show']) && $item['is_show'] == 1) {
					$item['status_name'] = '营业中';
				} else {
					$item['status_name'] = '已停业';
				}
				if ($item['day_start'] && $item['day_end']) {
					$start = date('H:i', strtotime($item['day_start']));
					$end = date('H:i', strtotime($item['day_end']));
					$item['day_time'] = implode(' - ', [$start, $end]);
				}
				$item['product'] = [];
				if ($product_num) {
					$item['product'] = $productServices->getSearchList($productWhere + ['type' => 1, 'relation_id' => $item['id']], 0, $product_num, ['id', 'type', 'relation_id', 'store_name', 'image', 'price'], 'sort desc,id desc', []);
				}
			}
		}
		return $limit == 1 ? ($storeList[0] ?? []) : $storeList;
	}


    /**
     * 附近门店
     * @param array $where
     * @param string $latitude
     * @param string $longitude
     * @param string $ip
     * @param int $limit
     * @param int $product_id
     * @param int $store_id
     * @return array|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getNearbyStore(array $where, string $latitude, string $longitude, string $ip = '', int $limit = 0, int $product_id = 0, int $store_id = 0)
    {
        $where = array_merge($where, ['status' => 1]);
        if ($limit) {
            $page = 1;
            $field = ['*'];
        } else {
            [$page, $limit] = $this->getPageValue();
            $field = ['id', 'name', 'phone', 'image', 'latitude', 'longitude', 'address', 'detailed_address', 'is_show', 'day_time', 'day_start', 'day_end', 'valid_range', 'delivery_type', 'use_system_money', 'is_alone', 'store_type'];
        }
        //默认附近门店
        $store_type = $where['store_type'] ?? 1;
        $uid = $where['uid'] ?? 0;
        unset($where['store_type'], $where['uid']);
        if ($store_type != 1) {//常用门店
            if ($uid) {
                /** @var StoreUserServices $storeUserServices */
                $storeUserServices = app()->make(StoreUserServices::class);
                $ids = $storeUserServices->getColumn(['uid' => $uid], 'store_id');
                if (!$ids) {
                    return [];
                }
                $where['ids'] = $ids;
            } else {//没登录，无常用门店
                return [];
            }
        }
        //移动端选择的门店
        if ($store_id) {
			$where['ids'] = [$store_id];
        }
        //该商品上架的门店
        if ($product_id) {
            /** @var StoreBranchProductServices $productServices */
            $productServices = app()->make(StoreBranchProductServices::class);
            [$ids, $applicableType] = $productServices->getApplicableStoreIds($product_id);
            if ($ids) {
                if ($store_id && !in_array($store_id, $ids)) {
					return [];
                }
                $where['ids'] = isset($where['ids']) && $where['ids'] ? array_intersect($where['ids'], $ids) : $ids;
            } else {
                if ($applicableType != 1) {//不是所有门店
                    return [];
                }
            }
        }
        $storeList = [];
        if (isset($where['ids'])) {
            $where['ids'] = array_unique(array_diff($where['ids'], [0]));
        }
        if (isset($where['id']) && $where['id']) {
            $storeList = $this->dao->getStoreList($where, $field, $page, $limit, [], $latitude, $longitude, $latitude && $longitude ? 1 : 0);
        } elseif ($latitude && $longitude) {
            $storeList = $this->dao->getStoreList($where, $field, $page, $limit, [], $latitude, $longitude, 1);
        } elseif ((isset($where['province']) && $where['province']) || (isset($where['city']) && $where['city']) || (isset($where['area']) && $where['area'])) {
            $storeList = $this->dao->getStoreList($where, $field, $page, $limit, [], $latitude, $longitude, $latitude && $longitude ? 1 : 0);
        } elseif ((isset($where['province']) && $where['province']) && (isset($where['city']) && $where['city']) && (isset($where['area']) && $where['area']) && !$latitude && !$longitude) {
            $storeList = $this->dao->getStoreList($where, $field, $page, $limit);
        } elseif ($ip) {
            $strField = $limit == 1 ? '*' : implode(',', $field);
            $addressArr = $this->addressHandle($this->convertIp($ip));
            $city = $addressArr['city'] ?? '';
            if ($city) {
                $storeList = $this->dao->getStoreByAddressInfo($city, $where, $strField, $page, $limit);
            }
            $province = $addressArr['province'] ?? '';
            if (!$storeList && $province) {
                $storeList = $this->dao->getStoreByAddressInfo($province, $where, $strField, $page, $limit);
            }
        }
        //上面条件都没获取到门店
        if (!$storeList) {
            $storeList = $this->dao->getStoreList($where, $field, $page, $limit);
        }
        if ($storeList) {
            foreach ($storeList as &$item) {
                $item['range'] = 0;
                if (isset($item['distance'])) {
                    $item['range'] = bcdiv($item['distance'], '1000', 1);
                } else {
                    $item['range'] = 0;
                }
                if (isset($item['is_show']) && $item['is_show'] == 1) {
                    $item['status_name'] = '营业中';
                } else {
                    $item['status_name'] = '已停业';
                }
                if ($item['day_start'] && $item['day_end']) {
                    $start = date('H:i', strtotime($item['day_start']));
                    $end = date('H:i', strtotime($item['day_end']));
                    $item['day_time'] = implode(' - ', [$start, $end]);
                }
            }
        }
        return $limit == 1 ? ($storeList[0] ?? []) : $storeList;
    }

    /**
     * 获取门店
     * @param array $where
     * @param array $field
     * @param string $latitude
     * @param string $longitude
     * @param int $product_id
     * @param array $with
     * @param int $type 0:普通1：秒杀
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getStoreList(array $where, array $field = ['*'], string $latitude = '', string $longitude = '', int $product_id = 0, array $with = [], int $type = 0, $store_id = 0)
    {
        [$page, $limit] = $this->getPageValue();
        $order = 0;
        if (isset($where['order_id']) && $where['order_id']) {
            /** @var StoreOrderServices $storeOrderServices */
            $storeOrderServices = app()->make(StoreOrderServices::class);
            $user_location = $storeOrderServices->value(['id' => $where['order_id']], 'user_location');
            [$longitude, $latitude] = explode(' ', $user_location);
        }
        if ($longitude && $latitude) {
            $order = 1;
        }
        $list = [];
        $count = 0;
        //移动端定位选择门店
        if ($store_id) {
            $where['ids'] = [$store_id];
        }
        //该商品上架的门店
        if ($product_id) {
            /** @var StoreBranchProductServices $productServices */
            $productServices = app()->make(StoreBranchProductServices::class);
            [$ids, $applicableType] = $productServices->getApplicableStoreIds($product_id, $type);
            if ($ids) {
                if ($store_id && !in_array($store_id, $ids)) {
					return compact('list', 'count');
                }
                $where['ids'] = isset($where['ids']) && $where['ids'] ? array_intersect($where['ids'], $ids) : $ids;
            } else {
                if ($applicableType != 1) {//不是所有门店
                    return compact('list', 'count');
                }
            }
        }
        $oid = (int)($where['order_id'] ?? 0);
        unset($where['order_id']);
        if (isset($where['ids'])) {
            $where['ids'] = array_unique(array_diff($where['ids'], [0]));
        }
        $storeList = $this->dao->getStoreList($where, $field, $page, $limit, $with, $latitude, $longitude, $order);
        if ($storeList) {
            $storeIds = [];
            if ($oid) {
                [$storeIds, $cartInfo] = $this->checkOrderProductShare($oid, $storeList, 2);
            }
            $prefix = config('admin.store_prefix');
			$regionIds = array_column($storeList, 'region_id');
			if ($regionIds) {
				/** @var SystemStoreRegionServices $storeRegionServices */
				$storeRegionServices = app()->make(SystemStoreRegionServices::class);
				$regionNames = $storeRegionServices->getColumn([['id', 'in', $regionIds], ['is_del', '=', 0]], 'name', 'id');
			}
            foreach ($storeList as &$item) {
                if (isset($item['distance'])) {
                    $item['range'] = bcdiv($item['distance'], '1000', 1);
                } else {
                    $item['range'] = 0;
                }
                if ($item['is_show'] == 1) {
                    $item['status_name'] = '营业中';
                } else {
                    $item['status_name'] = '已停业';
                }
                $item['prefix'] = $prefix;
				$item['recommend_region_name'] = '';
				if (isset($item['region_id']) && $item['region_id']) {
					$item['recommend_region_name'] = $regionNames[$item['region_id']] ?? '';
				}
                if ($oid) {
                    if (in_array($item['id'], $storeIds)) {
                        $list[] = $item;
                    }
                } else {
                    $list[] = $item;
                }
            }
            if (count($list) && $store_id) {
                $arr = [];
                foreach ($list as $key => $value) {
                    if ($value['id'] == $store_id) {
                        $arr = $value;
                        unset($list[$key]);
                    }
                }
                array_unshift($list, $arr);
            }
            if ($oid) {
                $count = count($list);
            } else {
                $count = $this->dao->count($where);
            }
        }

        return compact('list', 'count');
    }

    /**
     * 获取提货点头部统计信息
     * @return mixed
     */
    public function getStoreData()
    {
        $data['show'] = [
            'name' => '显示中的提货点',
            'num' => $this->dao->count(['type' => 0]),
        ];
        $data['hide'] = [
            'name' => '隐藏中的提货点',
            'num' => $this->dao->count(['type' => 1]),
        ];
        $data['recycle'] = [
            'name' => '回收站的提货点',
            'num' => $this->dao->count(['type' => 2])
        ];
        return $data;
    }

    /**
     * 门店重置账号密码表单
     * @param int $id
     * @return array
     * @throws \FormBuilder\Exception\FormBuilderException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function storeAdminAccountForm(int $id)
    {
        $storeInfo = $this->getStoreInfo($id);
        /** @var SystemStoreStaffServices $staffServices */
        $staffServices = app()->make(SystemStoreStaffServices::class);
        $staffInfo = $staffServices->getOne(['store_id' => $storeInfo['id'], 'level' => 0, 'is_admin' => 1, 'is_manager' => 1]);
        $field[] = $this->builder->hidden('staff_id', $staffInfo['id'] ?? 0);
        $field[] = $this->builder->input('phone', '管理员手机号', $staffInfo['phone'] ?? '')->col(24)->required('请输入手机号')->info('重置手机号：会同步清除之前绑定的商城用户信息，需要在门店后台重新编辑绑定');
        $field[] = $this->builder->input('account', '管理员账号', $staffInfo['account'] ?? '')->col(24)->required('请输入账号');
        $field[] = $this->builder->input('password', '登录密码')->type('password')->col(24)->required('请输入密码');
        $field[] = $this->builder->input('true_password', '确认密码')->type('password')->col(24)->required('请再次确认密码');
        return create_form('门店重置账号密码', $field, $this->url('/store/store/reset_admin/' . $id));
    }

    /**
     * 门店重置账号、密码、管理员手机号
     * @param int $id
     * @param array $data
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function resetAdmin(int $id, array $data)
    {
        $storeInfo = $this->getStoreInfo((int)$id);
        if (!$storeInfo) {
            throw new ValidateException('门店数据不存在');
        }
        /** @var SystemStoreStaffServices $staffServices */
        $staffServices = app()->make(SystemStoreStaffServices::class);
        $staffInfo = $staffServices->get((int)$data['staff_id']);
        if (!$staffInfo) {
            throw new ValidateException('门店管理员数据不存在');
        }
        $staff_data = [];
        if ($data['phone'] != $storeInfo['phone']) {//重置手机号 清空之前绑定
            $staff_data['uid'] = 0;
        }
        $staff_data['account'] = $data['account'];
        $staff_data['phone'] = $data['phone'];
        $staff_data['store_id'] = $storeInfo['id'];
        $staff_data['level'] = 0;
        $staff_data['is_admin'] = 1;
        $staff_data['verify_status'] = 1;
        $staff_data['is_manager'] = 1;
        $staff_data['is_cashier'] = 1;
		$staff_data['is_del'] = 0;
        $staff_data['add_time'] = time();
        $staff_data['pwd'] = $this->passwordHash($data['password']);
        $staffInfo = $staffServices->getOne(['account' => $data['account'], 'is_del' => 0]);
        if ($data['staff_id'] && $staffServices->getCount(['id' => $data['staff_id'], 'is_del' => 0])) {
            if ($staffInfo && $staffInfo['id'] != $data['staff_id']) {
                throw new ValidateException('该账号已存在');
            }
            if (!$staffServices->update($data['staff_id'], $staff_data)) {
                throw new ValidateException('创建门店管理员失败！');
            }
        } else {
            if ($staffInfo) {
                throw new ValidateException('该账号已存在');
            }
            if (!$staffServices->save($staff_data)) {
                throw new ValidateException('创建门店管理员失败！');
            }
        }
        return true;
    }

    /**
     * 获取erp门店列表
     * @return array|mixed
     * @throws \Exception
     */
    public function erpShopList()
    {
        [$page, $limit] = $this->getPageValue();
        if (!sys_config('erp_open')) {
            return [];
        }
        try {
            /** @var Erp $erpService */
            $erpService = app()->make(Erp::class);
            $res = Cache::tag('erp_shop')->remember('list_' . $page . '_' . $limit, function () use ($page, $limit, $erpService) {
                return $erpService->serviceDriver('Comment')->getShopList($page, $limit);
            }, 60);
        } catch (\Throwable $e) {
            Log::error([
                'message' => '读取ERP门店信息失败',
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);
        }

        return $res['data']['datas'] ?? [];
    }

    /**
     * 保存或修改门店
     * @param int $id
     * @param array $data
     * @param array $staff_data
     * @return mixed
     */
    public function saveStore(int $id, array $data, array $staff_data = [])
    {
		if (isset($data['region_id']) && $data['region_id']) {
			/** @var SystemStoreRegionServices $regionServices */
			$regionServices = app()->make(SystemStoreRegionServices::class);
			$region = $regionServices->get(['id' => $data['region_id'], 'is_del' => 0]);
			if (!$region) {
				throw new AdminException('选择区域无效或已删除！');
			}
			$data['is_region_alone'] = $region['is_alone'];
		}
        return $this->transaction(function () use ($id, $data, $staff_data) {
            $is_synchronous = 0;
            if ($id) {
                $is_new = 0;
                if ($data['product_category_status']) {
                    $categoryServices = app()->make(StoreProductCategoryServices::class);
                    $is_synchronous = $categoryServices->isStoreCate($id) ? 0 : 1;
                }
                if ($this->dao->update($id, $data)) {
                    return [$id, $is_new, $is_synchronous];
                } else {
                    throw new AdminException('修改失败或者您没有修改什么！');
                }
            } else {
                $is_new = 1;
                $data['add_time'] = time();
                if ($data['product_category_status']) {
                    $is_synchronous = 1;
                }
                if ($res = $this->dao->save($data)) {
                    if ($staff_data) {
                        $staffServices = app()->make(SystemStoreStaffServices::class);
                        if ($staffServices->count(['phone' => $staff_data['phone'], 'is_del' => 0])) {
                            throw new AdminException('该手机号已经存在');
                        }
                        if ($staffServices->count(['account' => $staff_data['account'], 'is_del' => 0])) {
                            throw new AdminException('管理员账号已存在');
                        }
                        $staff_data['level'] = 0;
                        $staff_data['store_id'] = $res->id;
                        $staff_data['is_admin'] = 1;
                        $staff_data['is_store'] = 1;
                        $staff_data['verify_status'] = 1;
                        $staff_data['is_manager'] = 1;
                        $staff_data['is_cashier'] = 1;
                        $staff_data['add_time'] = time();
                        $staff_data['pwd'] = $this->passwordHash($staff_data['pwd']);
                        if (!$staffServices->save($staff_data)) {
                            throw new AdminException('创建门店管理员失败！');
                        }
                        $data = [
                            ['role_name' => '店员', 'type' => 1, 'relation_id' => $res->id, 'status' => 1, 'level' => 1, 'rules' => '1048,1049,1097,1098,1099,1100,1050,1051,1101,1102,1103,1273,1274,1275,1276,1081,1104,1105,1106,1052,1054,1086,1129,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1130,1166,1167,1168,1169,1131,1170,1171,1172,1173,1174,1175,1176,1242,1088,1122,1123,1124,1125,1126,1127,1164,1165,1053,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1280'],
                            ['role_name' => '管理员', 'type' => 1, 'relation_id' => $res->id, 'status' => 1, 'level' => 1, 'rules' => '1048,1049,1097,1098,1099,1100,1050,1051,1101,1102,1103,1273,1274,1275,1276,1081,1104,1105,1106,1052,1054,1086,1129,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1130,1166,1167,1168,1169,1131,1170,1171,1172,1173,1174,1175,1176,1242,1088,1122,1123,1124,1125,1126,1127,1164,1165,1053,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1280,1055,1056,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1277,1057,1193,1194,1195,1196,1197,1249,1250,1251,1252,1253,1254,1058,1059,1060,1198,1199,1200,1243,1255,1256,1257,1258,1259,1260,1061,1201,1241,1062,1063,1215,1218,1219,1220,1244,1261,1262,1263,1264,1265,1064,1216,1217,1202,1065,1066,1203,1214,1067,1204,1212,1213,1235,1068,1205,1206,1069,1207,1208,1070,1089,1071,1209,1210,1211,1072,1073,1082,1083,1084,1085,1228,1229,1230,1231,1232,1233,1234,1236,1245,1246,1247,1248,1221,1222,1223,1224,1225,1226,1227,1266,1267,1268,1269,1270,1271,1272,1237,1238,1239,1240']
                        ];
                        /** @var SystemRoleServices $systemRoleServices */
                        $systemRoleServices = app()->make(SystemRoleServices::class);
                        $systemRoleServices->saveAll($data);
                    }
                    return [(int)$res->id, $is_new, $is_synchronous];
                } else {
                    throw new AdminException('保存失败！');
                }
            }
        });
    }

    /**
     * 获取门店缓存
     * @param int $id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @author 等风来
     * @email 136327134@qq.com
     * @date 2022/11/21
     */
    public function getStoreDisposeCache(int $id, string $felid = '')
    {
        $storeInfo = $this->dao->cacheRemember($id, function () use ($id) {
            $storeInfo = $this->dao->get($id);
            return $storeInfo ? $storeInfo->toArray() : null;
        });

        if ($felid) {
            return $storeInfo[$felid] ?? null;
        }

        if ($storeInfo) {
            $storeInfo['latlng'] = $storeInfo['latitude'] . ',' . $storeInfo['longitude'];
            $storeInfo['dataVal'] = $storeInfo['valid_time'] ? explode(' - ', $storeInfo['valid_time']) : [];
            $storeInfo['timeVal'] = $storeInfo['day_time'] ? explode(' - ', $storeInfo['day_time']) : [];
            $storeInfo['address2'] = $storeInfo['address'] ? explode(',', $storeInfo['address']) : [];
            return $storeInfo;
        }
        return [];
    }

    /**
     * 后台获取提货点详情
     * @param int $id
     * @param string $felid
     * @return array|false|mixed|string|string[]|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getStoreDispose(int $id, string $felid = '')
    {
        if ($felid) {
            return $this->dao->value(['id' => $id], $felid);
        } else {
            $storeInfo = $this->dao->get($id);
            if ($storeInfo) {
                $storeInfo['latlng'] = $storeInfo['latitude'] . ',' . $storeInfo['longitude'];
                $storeInfo['dataVal'] = $storeInfo['valid_time'] ? explode(' - ', $storeInfo['valid_time']) : [];
                $storeInfo['timeVal'] = $storeInfo['day_time'] ? explode(' - ', $storeInfo['day_time']) : [];
                $storeInfo['address2'] = $storeInfo['address'] ? explode(',', $storeInfo['address']) : [];
				$storeInfo['addressSelect'] = [$storeInfo['province'], $storeInfo['city'], $storeInfo['area'], $storeInfo['street']];
                return $storeInfo;
            }
            return false;
        }
    }


    /**
     * 平台门店运营统计
     * @param array $time
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function storeChart(array $time)
    {
        $list = $this->dao->getStoreList(['is_del' => 0, 'is_show' => 1], ['id', 'name', 'image']);
        /** @var StoreUserServices $storeUserServices */
        $storeUserServices = app()->make(StoreUserServices::class);
        /** @var BranchOrderServices $orderServices */
        $orderServices = app()->make(BranchOrderServices::class);
        /** @var StoreFinanceFlowServices $storeFinancFlowServices */
        $storeFinancFlowServices = app()->make(StoreFinanceFlowServices::class);
        $where = ['time' => $time];
        $order_where = ['paid' => 1, 'pid' => 0, 'is_del' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]];
        foreach ($list as &$item) {
            $store_where = ['store_id' => $item['id']];
            $item['store_price'] = (float)bcsub((string)$storeFinancFlowServices->sum($where + $store_where + ['trade_type' => 1, 'no_type' => 1, 'pm' => 1, 'is_del' => 0], 'number', true), (string)$storeFinancFlowServices->sum($where + $store_where + ['trade_type' => 1, 'no_type' => 1, 'pm' => 0, 'is_del' => 0], 'number', true), 2);
            $item['store_product_count'] = $orderServices->sum($where + $store_where + $order_where, 'total_num', true);
            $item['store_order_price'] = $orderServices->sum($where + $store_where + $order_where, 'pay_price', true);
            $item['store_user_count'] = $storeUserServices->count($where + $store_where);
        }
        return $list;
    }

    /**
     * 检测订单商品门店是否支持配送
     * @param int $oid
     * @param array $storeList
     * @param int $getType
     * @return array
     */
    public function checkOrderProductShare(int $oid, array $storeList, int $getType = 1)
    {
        if (!$oid || !$storeList) {
            return [[], []];
        }
        /** @var StoreOrderServices $storeOrderServices */
        $storeOrderServices = app()->make(StoreOrderServices::class);
        $orderInfo = $storeOrderServices->get($oid, ['id', 'order_id', 'uid', 'store_id', 'supplier_id']);
        /** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
        $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
        $cart_info = $storeOrderCartInfoServices->getSplitCartList($oid, 'cart_info');
        if (!$cart_info) {
            return [[], []];
        }

        /** @var StoreProductAttrValueServices $skuValueServices */
        $skuValueServices = app()->make(StoreProductAttrValueServices::class);
        $platProductIds = [];
        $platStoreProductIds = [];
        $storeProductIds = [];
        foreach ($cart_info as $cart) {
            $productInfo = $cart['productInfo'] ?? [];
            if (isset($productInfo['store_delivery']) && !$productInfo['store_delivery']) {//有商品不支持门店配送
                return [[], $cart_info];
            }
            switch ($productInfo['type'] ?? 0) {
                case 0://平台
                case 2://供应商
                    $platProductIds[] = $cart['product_id'];
                    break;
                case 1://门店
                    if ($productInfo['pid']) {//门店自有商品
                        $storeProductIds[] = $cart['product_id'];
                    } else {
                        $platStoreProductIds[] = $cart['product_id'];
                    }
                    break;

            }
        }
        /** @var StoreBranchProductServices $branchProductServics */
        $branchProductServics = app()->make(StoreBranchProductServices::class);
        //转换成平台商品
        if ($platStoreProductIds) {
            $ids = $branchProductServics->getStoreProductIds($platStoreProductIds);
            $platProductIds = array_merge($platProductIds, $ids);
        }
        $productCount = count($platProductIds);
        $result = [];
        foreach ($storeList as $store) {
            if ($storeProductIds && $store['id'] != $orderInfo['store_id']) {
                continue;
            }
            $is_show = $productCount == $branchProductServics->count(['pid' => $platProductIds, 'is_show' => 1, 'is_del' => 0, 'type' => 1, 'relation_id' => $store['id']]);//商品没下架 && 库存足够
            if (!$is_show) {
                continue;
            }
            $stock = true;
            foreach ($cart_info as $cart) {
                $productInfo = $cart['productInfo'] ?? [];
                if (!$productInfo) {
                    $stock = false;
                    break;
                }
                $applicable_store_ids = [];
                //验证商品适用门店
                if (isset($productInfo['applicable_store_id'])) $applicable_store_ids = is_array($productInfo['applicable_store_id']) ? $productInfo['applicable_store_id'] : explode(',', $productInfo['applicable_store_id']);
                if (!isset($productInfo['applicable_type']) || $productInfo['applicable_type'] == 0 || ($productInfo['applicable_type'] == 2 && !in_array($store['id'], $applicable_store_ids))) {
                    $stock = false;
                    break;
                }
                switch ($cart['type']) {
                    case 0:
                    case 6:
                    case 8:
                    case 9:
                    case 10:
                        $suk = $skuValueServices->value(['unique' => $cart['product_attr_unique'], 'product_id' => $cart['product_id'], 'type' => 0], 'suk');
                        break;
                    case 1:
                    case 2:
                    case 3:
                    case 5:
                    case 7:
                        $suk = $skuValueServices->value(['unique' => $cart['product_attr_unique'], 'product_id' => $cart['activity_id'], 'type' => $cart['type']], 'suk');
                        break;
                }
                $branchProductInfo = $branchProductServics->isValidStoreProduct((int)$cart['product_id'], $store['id']);
                if (!$branchProductInfo) {
                    $stock = false;
                    break;
                }
                $attrValue = $skuValueServices->get(['suk' => $suk, 'product_id' => $branchProductInfo['id'], 'type' => 0]);
                if (!$attrValue) {
                    $stock = false;
                    break;
                }
                if ($cart['cart_num'] > $attrValue['stock']) {
                    $stock = false;
                    break;
                }
            }
            if ($stock) {
                if ($getType == 1) {//只取一条门店数据
                    $result[] = $store['id'];
                    break;
                } else {
                    $result[] = $store['id'];
                }
            }
        }
        return [$result, $cart_info];
    }

    /**
     * 计算距离
     * @param string $user_lat
     * @param string $user_lng
     * @param string $store_lat
     * @param $store_lng
     * @return string
     */
    public function distance(string $user_lat, string $user_lng, string $store_lat, $store_lng)
    {
        try {
            return (round(6378137 * 2 * asin(sqrt(pow(sin((($store_lat * pi()) / 180 - ($user_lat * pi()) / 180) / 2), 2) + cos(($user_lat * pi()) / 180) * cos(($store_lat * pi()) / 180) * pow(sin((($store_lng * pi()) / 180 - ($user_lng * pi()) / 180) / 2), 2)))));
        } catch (\Throwable $e) {
            return '0';
        }
    }

    /**
     * 验证门店配送范围
     * @param int $store_id
     * @param array $addressInfo
     * @param string $latitude
     * @param string $longitude
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function checkStoreDeliveryScope(int $store_id, array $addressInfo = [], string $latitude = '', string $longitude = '')
    {
        //没登录 ｜｜ 无添加地址 默认不验证
        if (!$store_id || $store_id == -1 || (!$addressInfo && (!$latitude || !$longitude))) {
            return true;
        }
        //门店是否开启
        if (!sys_config('store_func_status', 1)) {
            return false;
        }
        $store_delivery_scope = (int)sys_config('store_delivery_scope', 0);
        if (!$store_delivery_scope) {//配送范围不验证
            return true;
        }
        $storeInfo = $this->getStoreDisposeCache($store_id);
        if (!$storeInfo) {
            return false;
        }
        if ($addressInfo) {
            $user_lat = $addressInfo['latitude'] ?? '';
            $user_lng = $addressInfo['longitude'] ?? '';
            if (!$user_lat || !$user_lng) {
                try {
                    $user_address = $addressInfo['province'] . $addressInfo['city'] . $addressInfo['district'] . $addressInfo['street'] . $addressInfo['detail'];
                    /** @var StoreDeliveryOrderServices $deliveryServices */
                    $deliveryServices = app()->make(StoreDeliveryOrderServices::class);
                    $addres = $deliveryServices->lbs_address($addressInfo['city'], $user_address);
                    $user_lat = $addres['location']['lat'] ?? '';
                    $user_lng = $addres['location']['lng'] ?? '';
                } catch (\Exception $e) {
                    return false;
                }
            }
        } else {
            $user_lat = $latitude;
            $user_lng = $longitude;
        }

        $distance = $this->distance($user_lat, $user_lng, $storeInfo['latitude'], $storeInfo['longitude']);

        return $distance <= $storeInfo['valid_range'];
    }
}
